import paramiko
import sys
import os
import platform
import stat
import socket
import logging
import time


def download_recursive(sftp, remote_path, local_path):
    """递归下载远程目录下的所有文件到本地"""
    for entry in sftp.listdir_attr(remote_path):
        mode = entry.st_mode
        remote_file = remote_path + "/" + entry.filename
        # remote_file = os.path.join(remote_path, entry.filename)
        local_file = os.path.join(local_path, entry.filename)
        
        if stat.S_ISDIR(mode):  # 如果是目录
            os.makedirs(local_file, exist_ok=True)
            download_recursive(sftp, remote_file, local_file)
        else:  # 如果是文件
            # logging.info(remote_file+"---"+local_file)
            sftp.get(remote_file, local_file)

class TesterControls(object):
    """对tester的常用动作，主要是更新fw，就没修改文件名"""
    def __init__(self,ip="",username="root",password="123456",slot="1"):
        self.username = username
        self.password = password
        self.slot = slot
        if ip == "" :
            self.ip_address = self.get_ip()
        else:
            self.ip_address = ip

        self.timeout = 1
        self.ssh = self.connection()
        if self.username == "root":
            self.sudoMix = "123456"
        else:
            self.sudoMix = "mixadmin"



    def get_ip(self) -> str:
        sw1 = str(self.slot)[-1]
        sw2 = ('00'+str(self.slot))[-2]
        ip_address = 32 + int(sw2)*16 + int(sw1)
        ip_address ="169.254.1."+str(ip_address)
        logging.info('MASK '+str(sw2)+str(sw1)+'  IP:'+str(ip_address))
        return ip_address

    def connection(self) -> paramiko.SSHClient:

        # if not self.check_ip_reachable():
        #     logging.info("The IP address is not reachable. Cannot establish SSH connection.")
        #     return None

        # 创建SSH客户端并连接
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            ssh.connect(self.ip_address, username=self.username, password=self.password,timeout=2)
            return ssh
        except Exception as e:
            logging.warning(f"{self.ip_address}-SSH连接失败: {e}")
            return None

    def check_ip_reachable(self) -> bool:
        """
        检查IP地址是否可达。
        :return: 如果IP地址可ping通则返回True，否则返回False。
        """
        if platform.system().lower() == 'windows':
            # Windows下的ping命令
            response = os.system("ping -n 1 -w 1000 " + self.ip_address)
        else:
            # Linux/macOS下的ping命令
            response = os.system("ping -c 1 -W 1 " + self.ip_address)
        
        # os.system的返回值，0通常表示成功
        return response == 0

    def push_file_to_ip(self,local_file_path,remote_file_path="/var/fw_update/upload"):
        if local_file_path == "":
            return True

        # 确保远程路径以斜杠结尾
        if not remote_file_path.endswith('/'):
            remote_file_path += '/'

        # logging.info(local_file_path)
        # if os.path.isdir(local_file_path):
        #     logging.info("123")
        # logging.info(remote_file_path)

        # 使用SFTP协议上传本地文件到远程服务器
        try:
            if self.ssh is None:
                return True

            with self.ssh.open_sftp() as sftp:
                # 如果是文件夹 - 递归上传
                if os.path.isdir(local_file_path):
                    if local_file_path[-1] == "/":
                        folder_name = os.path.basename(local_file_path[0:-1])
                    else:
                        folder_name = os.path.basename(local_file_path)
                    remote_dir = remote_file_path + folder_name
                    # 创建远程目录（如果不存在）
                    try:
                        sftp.mkdir(remote_dir)
                        # print("mkdir "+remote_dir)
                        # self.run_command_with_sudo("mkdir "+remote_dir)
                        print("mkdir "+remote_dir+"ok") 
                    except OSError:  # 目录已存在
                        print("mkdir "+remote_dir+"fail")  
                    for item in os.listdir(local_file_path):
                        local_item = os.path.join(local_file_path, item)
                        self.push_file_to_ip(local_item, remote_dir)
                    logging.warning(f"{self.ip_address} - {local_file_path} pushed to {remote_dir} ok ")
                    print(f"Folder {local_file_path} successfully pushed to {remote_dir}")
                    return True
                # 如果是文件 - 直接上传
                elif os.path.isfile(local_file_path):
                    remote_file_name = os.path.basename(local_file_path)
                    full_remote_path = remote_file_path + remote_file_name
                    sftp.put(local_file_path, full_remote_path)
                    logging.warning(f"{self.ip_address} - {local_file_path} pushed to {full_remote_path} ok ")
                    print(f"File {local_file_path} successfully pushed to {full_remote_path}")
                    return True
                else:
                    logging.error(f"Invalid path: {local_file_path}")
                    return False

        except Exception as e:
            logging.info(f"Error during file transfer: {e}")
            return False

    # def send_reboot(self):

    #     try:
    #         if self.ssh is None:
    #             return True
    #         # stdin,stdout,stderr = ssh.exec_command("cd /mix; ls -l")
    #         stdin,stdout,stderr = self.ssh.exec_command("reboot",timeout=1)
    #         output=stdout.read().decode("utf-8")
    #         error=stderr.read().decode("utf-8")
    #         logging.info("output:"+output)
    #         logging.info("error:"+error)
    #         logging.info("Reboot command sent to remote server.")
    #     except Exception as e:
    #         logging.info(f"Error sending reboot command: {e}")

    #     # stdin,stdout,stderr = ssh.exec_command("ls")
    #     # output=stdout.read().decode("utf-8")
    #     # logging.info(output )
    #     return True

    def delete_file(self,file_name,local_file_path):
        if file_name == "":
            return True
        test_file_path = local_file_path+file_name
        try:
            if self.ssh != None:
                stdin,stdout,stderr = self.ssh.exec_command("rm "+test_file_path,timeout=5)
                # stdin,stdout,stderr = self.ssh.exec_command("reboot",timeout=5)
                output=stdout.read().decode("utf-8")
                error=stderr.read().decode("utf-8")
                logging.info("output:"+output)
                logging.info("error:"+error)
                logging.info("Reboot command sent to remote server.")
            else:
                return True
        except Exception as e:
            logging.info(f"Error sending reboot command: {e}")

        # stdin,stdout,stderr = ssh.exec_command("ls")
        # output=stdout.read().decode("utf-8")
        # logging.info(output )
        return True

    def run_command(self,command):
        try:
            if self.ssh != None:
                stdin,stdout,stderr = self.ssh.exec_command(command,timeout=5)
                # stdin,stdout,stderr = self.ssh.exec_command("reboot",timeout=5)
                output=stdout.read().decode("utf-8")
                error=stderr.read().decode("utf-8")
                # print("output:"+output)
                # print("error:"+error)
                # logging.info("output:"+output)
                # logging.info("error:"+error)
                # logging.info("Reboot command sent to remote server.")
            else:
                return True
        except Exception as e:
            logging.info(f"Error sending reboot command: {e}")

        # stdin,stdout,stderr = ssh.exec_command("ls")
        # output=stdout.read().decode("utf-8")
        # logging.info(output )
        return True

    def wait_for_reboot(self, timeout=120, check_interval=10):
        start_time = time.time()
        while time.time() - start_time < timeout:
            if self.connection():  # 尝试重新连接
                # 验证系统已启动
                _, stdout, _ = self.ssh.exec_command("echo '系统已启动'", timeout=5)
                if "系统已启动" in stdout.read().decode():
                    return True
            time.sleep(check_interval)
        return False

    def run_command_with_sudo(self, command, sudo_password = "", timeout=5):
        try:
            if sudo_password == "":
                sudo_password = self.sudoMix
            if self.ssh is None:
                logging.error("SSH connection not established")
                return False
            
            # 获取传输对象并创建通道
            transport = self.ssh.get_transport()
            channel = transport.open_session()
            channel.get_pty()  # 请求伪终端
            
            # 设置通道超时时间（单位：秒）
            channel.settimeout(timeout)
            
            # 执行命令
            channel.exec_command(f"sudo -S {command}")  # -S 允许从stdin读取密码
            
            # 发送密码（末尾加\n模拟回车）
            channel.send(f"{sudo_password}\n")
            
            if command == "reboot":
                time.sleep(2)  # 确保命令已发送
                channel.close()  # 主动关闭通道
                self.ssh.close()  # 关闭SSH连接
                self.ssh = None  # 重置连接对象
                self.wait_for_reboot(10,0.1)
                return None

            # 创建缓冲区
            output_buffer = []
            error_buffer = []
            
            # 读取输出（带超时控制）
            while not channel.exit_status_ready():
                # 读取标准输出
                if channel.recv_ready():
                    output_buffer.append(channel.recv(4096).decode('utf-8'))
                
                # 读取错误输出
                if channel.recv_stderr_ready():
                    error_buffer.append(channel.recv_stderr(4096).decode('utf-8'))
            
            # 确保读取所有剩余数据
            while channel.recv_ready():
                output_buffer.append(channel.recv(4096).decode('utf-8'))
            while channel.recv_stderr_ready():
                error_buffer.append(channel.recv_stderr(4096).decode('utf-8'))
            
            # 获取退出状态
            exit_status = channel.recv_exit_status()
            
            # 合并输出
            output = ''.join(output_buffer)
            error = ''.join(error_buffer)
            
            # 记录日志
            logging.info(f"Command output: {output}")
            logging.info(f"Command error: {error}")
            logging.info(f"Exit status: {exit_status}")
            
            return exit_status == 0
            
        except socket.timeout:
            logging.error(f"Command timed out after {timeout} seconds")
            return False
        except Exception as e:
            logging.error(f"Error executing command: {str(e)}")
            return False

    def get_log_to_local(self, local_file_path="/Users/cyg/Desktop", remote_file_path="/var/log/bmu"):
        if local_file_path == "":
            return True
        try:
            if self.ssh != None:
                local_file_path = os.path.join(local_file_path, str(self.ip_address[-2:]))
                os.makedirs(local_file_path, exist_ok=True)
                with self.ssh.open_sftp() as sftp:
                    for entry in sftp.listdir_attr(remote_file_path):
                        # remote_file = os.path.join(remote_file_path, entry.filename)
                        remote_file = remote_file_path + "/" + entry.filename
                        local_file = os.path.join(local_file_path, entry.filename)
                        mode = entry.st_mode  # 获取文件类型
                        
                        if stat.S_ISDIR(mode):  # 如果是目录
                            os.makedirs(local_file, exist_ok=True)
                            download_recursive(sftp, remote_file, local_file)
                        else:  # 如果是文件
                            # logging.info(remote_file+"---"+local_file)
                            sftp.get(remote_file, local_file)
            else:
                return True
        except Exception as e:
            logging.info(f"Error during file transfer: {e}")

    def __del__(self):
        if self.ssh != None:
            self.ssh.close()

if __name__ == '__main__':
    # 从命令行参数获取IP和本地文件路径
    # slot = sys.argv[1]
    # local_file = sys.argv[2]

    # 推送文件到指定IP并获取结果
    tester = TesterControls("192.168.99.35","mixadmin","mixadmin")
    success = tester.push_file_to_ip("/Users/gdlocal/tester_method/test","/mix")
    # # 打印推送结果和文件的MD5值
    logging.info(success)
    # tester.get_log_to_local(local_file)
    # tester.run_command_with_sudo("reboot","mixadmin")
